package com.kube.numberator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Numberator {

	private static final int width = 3;
	private String[] unitNames = { "", "thousand", "million", "billion", "trillion" };
	private String numberAsString = null;

	public Numberator(long number) {
		if (number == 0) {
			this.numberAsString = "0";
		} else
		if (number < 0) {
			throw new RuntimeException("Only positive numbers please!");
		} else
		if (number > 999999999999999L) {
			throw new RuntimeException("Number is too big!");
		} else
			this.numberAsString = new Long(number).toString();
	}

	private List<String> split(String number) {
		// Split the number into groups each 3 digits wide (units, thousands, millions, etc)
		List<String> l = new ArrayList<String>();
		int length = number.length();

		while (length > width) {
			l.add(number.substring(length - width, length));
			number = number.substring(0, length - width);
			length = number.length();
		}

		// Create final part (which may be less than 3 wide)
		if (length > 0) {
			l.add(number);
		}

		// Reverse the list so we have larger units first
		Collections.reverse(l);
		return l;
	}

	public String parse() {
		// Base case
		if (numberAsString == "0") return "zero";
		
		// Create a string buffer to capture final result
		StringBuffer allofit = new StringBuffer();

		// Split the number string into sections of 3 digits to represent
		// thousands, millions, etc
		List<String> tokenizedNumber = split(numberAsString);

		// The size of the list represents how many times we must loop, adding
		// the unit words as we go
		int size = tokenizedNumber.size();

		// Loop through each unit part of the number
		for (int position = 0; position < size; position++) {
			String num = (String) tokenizedNumber.get(position);
			int i = new Integer(num).intValue();
			if (position == (size - 1) && size != 1 && i != 0 && i < 100) {
				allofit.append("and ");
			}

			// Parse the hundreds/tens/units for each group
			allofit.append(parseHundreds(i));

			// If this is not the last unit, add the unit word
			if (i != 0 && position != (size - 1)) {
				allofit.append(" ");
				allofit.append(unitNames[size - (position + 1)]).append(" ");
			}
		}

		// return the resulting string, trimming the last space if required
		return allofit.toString().trim();

	}

	private String parseUnits(int unit) {
		switch (unit) {
		case 1:
			return "one";
		case 2:
			return "two";
		case 3:
			return "three";
		case 4:
			return "four";
		case 5:
			return "five";
		case 6:
			return "six";
		case 7:
			return "seven";
		case 8:
			return "eight";
		case 9:
			return "nine";
		default:
			return "";
		}

	}

	private String parseTens(int number) {
		if (number / 10 > 0) {
			// Base cases
			switch (number) {
			case 10:
				return "ten";
			case 11:
				return "eleven";
			case 12:
				return "twelve";
			case 13:
				return "thirteen";
			case 14:
				return "fourteen";
			case 15:
				return "fifteen";
			case 16:
				return "sixteen";
			case 17:
				return "seventeen";
			case 18:
				return "eighteen";
			case 19:
				return "nineteen";
			}

			// Exceptional cases for dashes between words, (e.g. 'thirty-five')
			String seperator = " ";
			int tens = number / 10;
			int units = number % 10;
			if (units > 0) {
				seperator = "-";
			}

			switch (tens) {
			case 2:
				return "twenty" + seperator + parseUnits(units);
			case 3:
				return "thirty" + seperator + parseUnits(units);
			case 4:
				return "fourty" + seperator + parseUnits(units);
			case 5:
				return "fifty" + seperator + parseUnits(units);
			case 6:
				return "sixty" + seperator + parseUnits(units);
			case 7:
				return "seventy" + seperator + parseUnits(units);
			case 8:
				return "eighty" + seperator + parseUnits(units);
			case 9:
				return "ninety" + seperator + parseUnits(units);
			}
			throw new RuntimeException("Cannot parse tens of value " + tens);
		} else {
			return parseUnits(number);
		}
	}

	private String parseHundreds(int number) {
		if (number / 100 > 0) {
			String seperator = "";
			int hundreds = number / 100;
			int tens = number % 100;
			// Add 'and' if necessary between words, (e.g. 'One hundred AND one')
			if (tens > 0) {
				seperator = " and ";
			}
			return parseUnits(hundreds) + " hundred" + seperator + parseTens(tens);
		} else {
			return parseTens(number);
		}
	}
}
